function me = ret
    global globals;
    InitializeGlobals();
    
    %p = CreateProperty(100000,100000,2000,500,1000,200);
    %p.mortgage = CreateMortgage(80000, 0.05, 30*12);

    me = InitializeMe();
   
    for month = 1:400        
        globals.month = month;
  
        me = Maintenance(me);

        for idx = 1:length(me.properties)            
            me.properties(idx) = UpdateProperty(me.properties(idx));
        end
        
        me = UpdateMe(me);
        me = PerformStrategy(me);

        
        UpdateGlobals();
        
    end

    clf;
    [me.properties.mortgage];
    balance = [ans.balance];
    plot([me.properties.value]-balance);
%     
%     plot(p.value-p.mortgage.balance,'b');
%     hold on;
%     plot(p.value,'r');
%     plot(p.mortgage.balance,'g');
end

%% Me Code
function x = InitializeMe()
    global globals;
    m = globals.month;
    x.properties = [];
    x.incomeTaxRate = 0.23;
    x.retirementSavingsRate = 0.15;   
    
    x.job.employed = 1;
    x.job.yearlySalary = 110000;
    
    x.job.monthlyGrossSalary = zeros(2,1);
    x.job.monthlyNetSalary = zeros(2,1);
    
    x.job.monthlyGrossSalary(m) = x.job.yearlySalary/12;
    x.job.monthlyNetSalary(m) = x.job.monthlyGrossSalary(m) * ...
                                (1 - x.incomeTaxRate - x.retirementSavingsRate);
       
    x.rentalIncome = zeros(2,1);
    
    x.personalExpenses = zeros(2,1);
    x.personalExpenses(m) = x.job.monthlyNetSalary(m) * 0.625;
    
    x.totalIncome = zeros(2,1);
    x.totalIncome(m) = x.rentalIncome(m) + x.job.monthlyNetSalary(m);
        
    x.netIncome = zeros(2,1);
    x.netIncome = x.totalIncome(m) - x.personalExpenses(m);
    
    x.savings = zeros(20,1);
    x.savings(m) = 20000;
end

function x = UpdateMe(me)
    global globals;
    m = globals.month;
    x = me;
    
    if m ~= 1 && mod(m,12) == 0
       % end of year party time!
       x.job.yearlySalary = x.job.yearlySalary * (1+globals.inflationRateYearly);
    end
    
    if x.job.employed
       x.job.monthlyGrossSalary(m) = x.job.yearlySalary/12;
       x.job.monthlyNetSalary(m) = x.job.monthlyGrossSalary(m) * ...
           ( 1 - x.incomeTaxRate - x.retirementSavingsRate );
    else
        x.job.monthlyGrossSalary(m) = 0;
        x.job.monthlyNetSalary(m) = 0;
    end
    
    if length(x.properties) > 0
        rInc = [x.properties.income];
        x.rentalIncome(m) = sum(rInc(m,:));
    else
        x.rentalIncome(m) = 0;
    end
    
    if globals.strategy == 3
       % use any rental income to pay off mortgages       
       for idx = 1:length(x.properties)
          if x.properties(idx).mortgage.paid == 0
              payment = min(x.rentalIncome(m), x.properties(idx).mortgage.balance(m));
              x.properties(idx).mortgage = MakeExtraMortgagePayment(x.properties(idx).mortgage, payment);
              x.rentalIncome(m) = x.rentalIncome(m) - payment;
          end
          if x.rentalIncome(m) <= 0
              break;
          end
       end
    end

    x.totalIncome(m) = x.rentalIncome(m) + x.job.monthlyNetSalary(m);
    x.personalExpenses(m) = x.job.monthlyNetSalary(m)*0.625;
    x.netIncome(m) = x.totalIncome(m) - x.personalExpenses(m);
    
    if m == 1
        x.savings(m) = x.savings(1) + x.netIncome(m);
    else
        x.savings(m) = x.savings(m-1) + x.netIncome(m);
    end
    
    if x.rentalIncome(m) > x.job.monthlyNetSalary(m)*1.1 && x.job.employed
        x.job.employed = 0;
        disp(['Quit job at month ' num2str(m)]);
    end
end

function x = Maintenance(me)
    % find tenants etc
    global globals;
    m = globals.month;
    x = me;
    
    for idx = 1:length(x.properties)
       p = x.properties(idx);
       if p.purchaseMonth == m
           continue
       end
       if p.tenant.vacant
           newTenant = FindTenant();
           if ~isempty(newTenant)
               p.tenant = newTenant;
           end
       else
           if rand < globals.vacancyRate
              p.tenant.vacant = 1; 
              p.tenant.rent(m) = 0;
           end
       end
       x.properties(idx) = p;
    end        
end

function x = PerformStrategy(oldme)
    global globals;
    x = oldme;
    m = globals.month;
        
    price = globals.averageHomePrice;
    
    %disp(sprintf('year %02d month %02d: savings = %f', floor((m-1)/12), mod(m-1,12)+1, x.savings(m)));
    
    % 20% for downpayment + 2% for closing costs = 22%    
    if x.savings(m) >= globals.averageHomePrice*0.22 && length(x.properties) < 30
        % let's buy!
        disp(['Buying house on month ' num2str(m)]);
        p = FindProperty();
        p.mortgage = CreateMortgage(p.purchasePrice*0.8, 0.05, 30*12);        
        if isempty(x.properties)
            x.properties = p;
        else
            x.properties(end+1) = p;
        end
        x.savings(m) = x.savings(m) - p.purchasePrice * 0.2;
    end
end

%% Utility Code
function x = CalculatePropertyCosts(p)
    global globals;
    m = globals.month;
    x = p.mortgage.payment(m) + p.taxCostMonthly(m) + p.insuranceCostMonthly(m) + p.maintenanceCostMonthly(m) + p.hoaCostMonthly(m);
end

function x = CalculatePayment(p)
    global globals;
    m = globals.month;
    if p.tenant.vacant
        % vacancy roh roh
        x = 0;
    else
        switch globals.strategy
            case {1,3}            
                % take all of this property's leftover rent and put toward
                % mortgage
                x = p.tenant.rent(m) - ...                    
                    p.taxCostMonthly(m) - ...
                    p.insuranceCostMonthly(m) - ...
                    p.maintenanceCostMonthly(m) - ...
                    p.hoaCostMonthly(m);
            case 2
                x = p.mortgage.monthlyPayment;
        end
    end

end

function mr = ConvertYearlyRateToMonthly(yr)
    mr = (1+yr)^(1/12)-1;
end

function InitializeGlobals()
    randseed(1);
    
    global globals;
    globals = struct;
    globals.month = 1;
    globals.inflationRateYearly = 0.02;
    globals.appreciationRateYearly = 0.02;
    globals.strategy = 1;
    globals.vacancyRate = 0.0;
    globals.averageMonthlyRent = 1000;
    globals.averageHomePrice = 100000;    
end

function UpdateGlobals()
    global globals;
    monthlyInflationRate = ConvertYearlyRateToMonthly(globals.inflationRateYearly);
    globals.averageMonthlyRent = (1+monthlyInflationRate)*globals.averageMonthlyRent;
    globals.averageHomePrice = (1+monthlyInflationRate)*globals.averageHomePrice;
end

%% Tenant Code
function x = CreateTenant(monthlyRent)
    global globals;
    x.vacant = 0;
    x.rent = zeros(2,1);
    x.rent(globals.month) = monthlyRent;
    x.startMonth = globals.month;
end

function x = UpdateTenant(tenant)
    global globals;
    x = tenant;
    m = globals.month;
    
    if x.vacant == 1
        x.rent(m) = 0;
        return
    end
       
    if m == tenant.startMonth
        return
    end
        
    if mod( m - tenant.startMonth, 12) == 0
        % jack up rent according to inflation
        x.rent(m) = (1+globals.inflationRateYearly)*x.rent(m-1);
    else
        x.rent(m) = x.rent(m-1);
    end
end

function x = FindTenant()
    global globals;
    x = [];
    if rand > globals.vacancyRate                
        x = CreateTenant(globals.averageMonthlyRent);
    end
end
%% Mortgage Code
function x = CreateMortgage(principal, rate, termMonths)
    global globals;
    m = globals.month;
    x.startDate = m;
    
    x.balance = zeros(2,1);
    x.balance(m) = principal;
    
    x.rate = rate;
    x.termMonths = termMonths;
    x.paid = 0;
    
    J = rate /12;
    x.monthlyPayment = principal * J / (1 - ( 1+J )^(-termMonths));
    
    x.payment = zeros(2,1);
    x.interest = zeros(2,1);
    x.principal = zeros(2,1);
end

function x = PayMortgage(mortgage, payment)
    global globals;
    m = globals.month;
    x = mortgage;
    
    I = round(((x.rate/12)*x.balance(m-1))*100)/100;
    P = round((payment - I)*100)/100;
    x.interest(m) = I;
    x.principal(m) = P;
    x.balance(m) = x.balance(m-1) - P;
    x.payment(m) = payment;
    if x.balance(m) < 0
        x.paid = 1;
        x.balance(m) = 0;
    end
end

function x = MakeExtraMortgagePayment(mortgage, payment)
    global globals;
    x = mortgage;
    m = globals.month;
    x.principal(m) = x.principal(m) + payment;
    x.balance(m) = x.balance(m) - payment;
    if x.balance(m) < 0
        x.paid = 1;
        x.balance(m) = 0;
    end
end

function x = UpdatePaidMortgage(mortgage)
    global globals;
    month = globals.month;
    x = mortgage;
    x.balance(month) = 0;
    x.interest(month) = 0;
    x.principal(month) = 0;
    x.payment(month) = 0;

end

%% Property Code
function p = CreateProperty(purchasePrice, value, tax, insurance, maintenance, hoa)
    global globals;  
    p.purchasePrice = purchasePrice;
    p.purchaseMonth = globals.month;
    p.value = zeros(2,1);
    p.value(globals.month) = value;
    
    p.taxYearly = tax;
    p.taxCostMonthly = zeros(2,1);
    p.taxCostMonthly(globals.month) = tax/12;
    
    p.insuranceYearly = insurance;
    p.insuranceCostMonthly = zeros(2,1);
    p.insuranceCostMonthly(globals.month) = insurance/12;
    
    p.maintenanceYearly = maintenance;
    p.maintenanceCostMonthly = zeros(2,1);
    p.maintenanceCostMonthly(globals.month) = maintenance/12;
    
    p.hoaYearly = hoa;
    p.hoaCostMonthly = zeros(2,1);
    p.hoaCostMonthly(globals.month) = hoa/12;
    
    p.income = zeros(2,1);
    p.income(globals.month) = 0;
    
    p.mortgage = struct('paid',1);
    p.tenant = struct('vacant',1);
    p.tenant.rent = zeros(2,1);
    p.tenant.rent(globals.month) = 0;
end

function p = UpdateProperty(prop)
    global globals;
    p = prop;
    m = globals.month;
    if m == p.purchaseMonth
        return
    end
    
    p.value(m) = (1+ConvertYearlyRateToMonthly(globals.appreciationRateYearly))*p.value(m-1);
    
    if mod(m - p.purchaseMonth, 12) == 0
        p.taxYearly = (1+globals.inflationRateYearly)*p.taxYearly;
        p.insuranceYearly = (1+globals.inflationRateYearly)*p.insuranceYearly;
        p.maintenanceYearly = (1+globals.inflationRateYearly)*p.maintenanceYearly;
        p.hoaYearly = (1+globals.inflationRateYearly)*p.hoaYearly;
    end
    
    p.taxCostMonthly(m) = p.taxYearly/12;
    p.insuranceCostMonthly(m) = p.insuranceYearly/12;
    p.maintenanceCostMonthly(m) = p.maintenanceYearly/12;
    p.hoaCostMonthly(m) = p.hoaYearly/12;
    
    p.tenant = UpdateTenant(p.tenant);
    
    if ~p.mortgage.paid && m ~= p.purchaseMonth
        payment = CalculatePayment(p);
        if payment > p.mortgage.balance(m-1)
            payment = p.mortgage.balance(m-1);
        end
        p.mortgage = PayMortgage(p.mortgage, payment);
    elseif p.mortgage.paid
        p.mortgage = UpdatePaidMortgage(p.mortgage);
    end
    p.income(m) = p.tenant.rent(m) - CalculatePropertyCosts(p);
    
end

function p = FindProperty()
    global globals;
    price = globals.averageHomePrice;
    p = CreateProperty(price, price, price*0.02, price*0.005, price*0.01, price*0.002);
end